#coding:utf-8
'''
Created on 2017-04-28

@author: sandy
'''

import logging
from twisted.web import resource
from twisted.web.server import Site
from twisted.internet import reactor
from twisted.web.server import NOT_DONE_YET
import sys
import json
import os
import time
import uuid
import shutil
import zlib
from pycommon import const,utils
import accountconfig
import configmanager

ENCODING = u"UTF-8"

def returnErrorAsync(request, ret, error):
    result = {}
    result[u"ret"] = ret
    result[u"err"] = unicode(error)
    request.write(json.dumps(result).encode(ENCODING))
    request.finish()
    
def returnRetAsync(request, result):
    result[u"ret"] = const.ERR_OK
    request.write(json.dumps(result).encode(ENCODING))
    request.finish()  
    
def buildError(ret, error):
    result = {}
    result[u"ret"] = ret
    result[u"err"] = unicode(error)
    return json.dumps(result).encode(ENCODING)
    
def buildRet(request, result):
    return json.dumps(result).encode(ENCODING) 

def login(request,params):
    account = params["account"]
    passwd = params["passwd"]
    code = accountconfig.instance.checkAccount(account,passwd)
    if code == const.ERR_OK:
        session = request.getSession().uid
        accountconfig.instance.addSession(account,session)
        cfglist = accountconfig.instance.getCfgListBySession(session)
    else:
        cfglist = []
    return buildRet(request,{"ret":code,"cfglist":cfglist})

def getcfgmap(request,params):
    cfgtype = params["cfgtype"]
    code = const.ERR_OK
    session = request.getSession().uid
    check = accountconfig.instance.checkSession(session)
    if check:
        res = {}
        for file in os.listdir(cfgtype):
            md = utils.md5file(cfgtype + os.sep + file)
            filename = file[0:-5]
            res[filename] = md
        return buildRet(request,{"ret":code,"cfgmap":res})
    else:
        code = const.ERR_SESSION_WRONG
        return buildRet(request,{"ret":code,"cfgmap":{}})
    
def download(request,params):
    cfgtype = params["cfgtype"]
    cfgname = params["cfgname"]
    session = request.getSession().uid
    account = accountconfig.instance.getAccount(session)
    check = accountconfig.instance.checkSession(session)
    if check:
        path = os.path.join(cfgtype,cfgname + u".json")
        exist = accountconfig.instance.checkCfgNamePermissions(account,path)
        if exist:
            with open(path,"rb") as f:
                content = f.read()
            return content
        else:
            code = const.ERR_PERMISSIONS
            return buildRet(request,{"ret":code,"msg":"cannot download this file"})
    else:
        code = const.ERR_SESSION_WRONG
        return buildRet(request,{"ret":code,"msg":"cannot download this file"})
    pass

def deletecfg(request,params):
    transname = params["transname"]
    cfgname = params["cfgname"]
    configmanager.instance.addTransaction(transname,cfgname,"")
    return buildRet(request,{"ret":const.ERR_OK})


def createuploadtrans(request,params):
    cfgtype = params["cfgtype"]
    code = const.ERR_OK
    timestr = time.strftime(u'%Y%m%d%H%M%S', time.localtime(time.time()))
    substr = utils.genSession()
    transname = "{0}_{1}_{2}".format(cfgtype,timestr,substr)
    return buildRet(request,{"ret":code,"transname":transname})
    pass


def commituploadtrans(request,params):
    transname = params["transname"]
    cfgtype = params["cfgtype"]
    addlist = params["addlist"]
    modifylist = params["modifylist"]
    dellist = params["dellist"]
    oldpath = "trans/{0}/old".format(transname)
    newpath = "trans/{0}/backup".format(transname)
    shutil.copytree(cfgtype,oldpath)
    trans = configmanager.instance.popTransaction(transname)
    for file in addlist:
        for t in trans:
            if t.cfgname == file:
                cachekey = cfgtype + "/" + file + ".json"
                with open(cachekey,"wb") as f:
                    f.write(t.content)
                logging.info("commituploadtrans addfile file[%s]",cachekey)
                trans.remove(t)
                break
        pass
    for file in modifylist:
        for t in trans:
            if t.cfgname == file:
                cachekey = cfgtype + "/" + file + ".json"
                with open(cachekey,"wb") as f:
                    f.write(t.content)
                logging.info("commituploadtrans update file[%s]",cachekey)
                cfgname = cfgtype + "." + file
                configmanager.instance.updateConfig(cfgname,t.content)
                trans.remove(t)
                break
            
        pass
    for file in dellist:
        for t in trans:
            if t.cfgname == file:
                delkey = cfgtype + "/" + file + ".json"
                os.remove(delkey)
                logging.info("commituploadtrans delfile file[%s]",delkey)
                trans.remove(t)
                break
        pass
    shutil.copytree(cfgtype,newpath)
    configmanager.instance.checkTimeOutTrans()
    return buildRet(request,{"ret":const.ERR_OK})

class PageManager(resource.Resource):
    
    def __init__(self):
        self.handlermap = {}
        
    def register(self, handlername ,func):
        logging.info(u'PageManager.register() handlername=%s', handlername)
        if self.handlermap.get(handlername, None)!=None:
            raise KeyError(u'PageManager<%d> hanlder exist' % (handlername,))
        self.handlermap[handlername] = func

    def render_GET(self, request):
        try:
            return self.handleRequest(request)
        except Exception, e:
            logging.exception("render_GET")
            return buildError(const.ERR_UNKNOWN, e)
    
    
    def handleRequest(self,request):
        request.setHeader('Access-Control-Allow-Origin', '*')
        request.setHeader('Access-Control-Allow-Methods', 'GET')
        request.setHeader('Access-Control-Allow-Headers', 'x-prototype-version,x-requested-with')
        request.setHeader('Access-Control-Max-Age', 2520) # 42 hours  
        method = request.args["method"][0]
        param = request.args["param"][0]
        method = method.decode(ENCODING)
        param = param.decode(ENCODING)
        param = json.loads(param)
        func = self.handlermap.get(method, None)
        if not func:
            m = sys.modules['website']
            func = getattr(m, method, None)
        if not func:
            return buildError(const.ERR_UNKOWN_METHOD, u"unkown method : %s" % method)
        session = request.getSession().uid
        account = accountconfig.instance.getAccount(session)
        logging.info("method=%s",method)
        logging.info("session=%s account=%s",session,account)
        logging.info("param=%s",param)
        ret = func(request, param)    
        if ret != NOT_DONE_YET:
            return ret
        return NOT_DONE_YET

    def render_POST(self, request):
        try:
            return self.handlePost(request)
        except Exception, e:
            logging.exception("render_POST")
            return buildError(const.ERR_UNKNOWN, e)

    def handlePost(self,request):
        contentlen = request.getHeader("Content-Length")
        contenttype = request.getHeader("Content-type")
        transname = request.getHeader("Trans-Name")
        cfgname = request.getHeader("Cfg-Name")
        content = zlib.decompress(request.content.read())
        session = request.getSession().uid
        account = accountconfig.instance.getAccount(session)
        logging.info("upload transname=%s cfgname=%s len=%s",transname,cfgname,contentlen)
        logging.info("session=%s account=%s",session,account)
        configmanager.instance.addTransaction(transname,cfgname,content)
        return buildRet(request,{"ret":const.ERR_OK})

class WebSite(object):
    
    def __init__(self):
        self.__port = None
        self.__root = None
        self.__regmap = {}
        
    def register(self, path, page):
        logging.info(u'WebSite.register() path=%s', path)
        if self.__root:
            self.__root.putChild(path, page)
        else:
            self.__regmap[path] = page
    
    def start(self,webip = u'127.0.0.1',webport = 80):
        self.__root = resource.Resource()
        self.__root.putChild("op", PageManager())
        self.__root.putChild("upload", PageManager())
        for path,page in self.__regmap.iteritems():
            self.__root.putChild(path, page)
        self.__regmap.clear()
        self.__port = reactor.listenTCP(webport,Site(self.__root),interface = webip)
    
    def stop(self):
        if self.__port:
            self.__port.stopListening()
            self.__port = None
    
    
instance = WebSite()
